/* m41t81.c

Copyright (C) Alex S. (mailto: md5sum@alexsp.ru)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details
http://www.gnu.org/licenses/gpl-2.0.html.

You should have received a copy of the GNU General Public License
along with this program in the file gpl-2.0.txt;
if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 *
 * mktime() function based on newlib mktime.c Author: G. Haley
 *
 */

#include "m41t81.h"
#include "global.h"
//#include <string.h>

#define M41_addr 0xD0
#define ifTWI(a) if (TWI_check(a)) {TWI_stop();TWCR=0;return ERROR;}

uint8_t m41_control=0;
//uint8_t m41_stop=0;
//uint8_t m41_halt=0;
uint8_t TWI_last_error;

static uint8_t timekeeper[20];

static void mktime ();
static void m41_parse();
static hw_ret_status m41_clear_halt();
uint8_t check_date(tm *tmptime);

static const int DAYS_IN_MONTH[12] ={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
#define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
static const int _DAYS_BEFORE_MONTH[12] ={0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
#define _ISLEAP(y) (((y) % 4) == 0 && (((y) % 100) != 0 || (((y)) % 400) == 0))
#define _DAYS_IN_YEAR(year) (_ISLEAP(year) ? 366 : 365)

hw_ret_status time_init() {
/*
	uint8_t i;
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x00);
	ifTWI(TW_MT_DATA_ACK);
	TWI_restart();
	ifTWI(TW_REP_START);
	TWI_send_data_nack(M41_addr|TW_READ);
	ifTWI(TW_MR_SLA_ACK);
	for (i=0;i<19;i++) {
		TWI_send_data_ack(0xFF);
	}
	TWI_send_data_nack(0xFF);
	TWI_stop();

*/
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x0a);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0x41);
	ifTWI(TW_MT_DATA_ACK);
	TWI_stop();
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x13);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0xf0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_stop();

//	return time_read();
	return SUCCESS;
}

hw_ret_status m41start(){
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x01);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_stop();
	return SUCCESS;
}

hw_ret_status time_read(tm *tmptime) {
	uint8_t i;

	/*Прочитаем текущее время*/
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x00);
	ifTWI(TW_MT_DATA_ACK);
	TWI_restart();
	ifTWI(TW_REP_START);
	TWI_send_data_nack(M41_addr|TW_READ);
	ifTWI(TW_MR_SLA_ACK);
	for (i=0;i<19;i++) {
		TWI_send_data_ack(0xFF);
		timekeeper[i]=TWDR;
	}
	TWI_send_data_nack(0xFF);
	timekeeper[19]=TWDR;
	TWI_stop();
	m41_parse(tmptime);
	mktime(tmptime);
	if (flags&(FLG_CLK_STOP)) {
//		flags&=~FLG_CLK_STOP;
//		flags&=~FLG_PWR_ERR;
		m41start();
		return CLOCK_STOP;
	}else if (flags&&FLG_PWR_ERR){
		return PWR_FAIL;
	}
#ifdef DBG_M41
	itoa(time.hour,debug_str,10);
	debug(debug_str);
	debug(":");
	itoa(time.min,debug_str,10);
	debug(debug_str);
	debug(":");
	itoa(time.sec,debug_str,10);
	debug(debug_str);
	debug(">>");
	itoa(time.unix_time,debug_str,16);
	debug(debug_str);
	debug("\r\n");
#endif
	return SUCCESS;
}

static void m41_parse(tm *tmptime) {
#define data timekeeper[addr]

	uint8_t addr;
	for (addr=0;addr<20;addr++) {
/*
		itoa(addr,debug_str,16);
		debug(debug_str);debug("->");
		itoa(data,debug_str,16);
		debug(debug_str);debug(",");
*/
		switch (addr) {
		case 1:
			if (data&(1<<7)) {flags|=FLG_CLK_STOP;}
			tmptime->sec=((data&0x70)>>4)*10 + (data&0x0f);
			if (tmptime->sec>59) tmptime->sec=0;
			break;
		case 2:
			tmptime->min=((data&0x70)>>4)*10 + (data&0x0f);
			if (tmptime->min>59) tmptime->min=0;
			break;
		case 3:
			tmptime->hour=((data&0x30)>>4)*10 + (data&0x0f);
			if (tmptime->hour>23) tmptime->hour=0;
			break;
		case 5:
			tmptime->day=((data&0x30)>>4)*10 + (data&0x0f);
			break;
		case 6:
			tmptime->month=(data&0x0f);
			if (data&0x10) tmptime->month+=10;
			if (tmptime->month>12) tmptime->month=1;
			break;
		case 7:
			tmptime->year=((data&0xf0)>>4)*10 + (data&0x0f);
			if (tmptime->year>99) tmptime->year=0;
			if (check_date(tmptime)) tmptime->day=1;
			break;
		case 8:
			m41_control=data&0x3f;
			break;
		case 0x0c:
			if (data&0x40) {
				flags|=FLG_PWR_ERR;
				//memcpy(&stop_time,&curr_time,sizeof(curr_time));
				//stop_time=curr_time;
				//mktime(&stop_time);
				m41_clear_halt();
			}
			break;

		default:
			break;
		}
	}
//	debug("\r\n");
#undef data
}

uint8_t check_date(tm *tmptime){
	uint8_t days_in_feb=28;
	if (_DAYS_IN_YEAR(tmptime->year+2000)==366) days_in_feb=29;
	if (tmptime->day>_DAYS_IN_MONTH(tmptime->month-1)) return _DAYS_IN_MONTH(tmptime->month-1);
	return 0;
}

static void mktime (tm * tmp_time) {

#define _SEC_IN_MINUTE 60L
#define _SEC_IN_HOUR 3600L
#define _SEC_IN_DAY 86400L

	uint32_t tim = 946674000UL-3600UL;
	long days = 0;
	uint8_t year;

	/* Считаем секунды за указанный день */
	tim += tmp_time->sec + (tmp_time->min * _SEC_IN_MINUTE) +
			(tmp_time->hour * _SEC_IN_HOUR);

	/* Считаем дни в указанном году */
	days += tmp_time->day - 1;
	days += _DAYS_BEFORE_MONTH[(tmp_time->month-1)];
	if ((tmp_time->month-1) > 1 && _DAYS_IN_YEAR (tmp_time->year+2000) == 366) days++;

	/*Считаем дней до указанного года, от 2000*/
	for (year=0;year < tmp_time->year; year++) days += _DAYS_IN_YEAR (year+2000);

	/* Вычисляем день недели */
	tmp_time->day_w = (uint8_t)((days+6) % 7);

	/* Вычисляем сколько всего секунд прошло с 01.01.2000 */
	tim += (days * _SEC_IN_DAY);
	tmp_time->unix_32=tim;
//	ultoa(tim,debug_str,10);
//	debug(debug_str);debug("\n");

	tmp_time->ten_m=tmp_time->hour*6+tmp_time->min/10;
}

hw_ret_status m41stop(){
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x01);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0x80);
	ifTWI(TW_MT_DATA_ACK);
	TWI_stop();
//	curr_time.sec=0;
	return SUCCESS;
}

hw_ret_status m41calibrate(uint8_t * m41_control){
	if (*m41_control==0x20) *m41_control=0;
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x08);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(*m41_control);
	ifTWI(TW_MT_DATA_ACK);
	TWI_stop();
	return SUCCESS;
}

hw_ret_status m41set_time(tm * curr_time){
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x00);
	ifTWI(TW_MT_DATA_ACK);

	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(((curr_time->sec/10)<<4)|(curr_time->sec%10));
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(((curr_time->min/10)<<4)|(curr_time->min%10));
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(((curr_time->hour/10)<<4)|(curr_time->hour%10));
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(1);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(((curr_time->day/10)<<4)|(curr_time->day%10));
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(((curr_time->month/10)<<4)|(curr_time->month%10));
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(((curr_time->year/10)<<4)|(curr_time->year%10));
	ifTWI(TW_MT_DATA_ACK);

	if (m41_control==0x20) m41_control=0;
	TWI_send_data_nack(m41_control);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0x41);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0x01);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0xf0);
	ifTWI(TW_MT_DATA_ACK);

	TWI_stop();
	return SUCCESS;
}

hw_ret_status m41_get_calibrate(uint8_t * tmp){
/*	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x08);
	ifTWI(TW_MT_DATA_ACK);
	TWI_restart();
	ifTWI(TW_REP_START);
	TWI_send_data_nack(M41_addr|TW_READ);
	ifTWI(TW_MR_SLA_ACK);
	TWI_send_data_nack(0xFF);
//	m41_parse(8,TWDR);
	TWI_stop();
	*tmp = TWDR;*/
	*tmp=m41_control;
	return SUCCESS;
}

static hw_ret_status m41_clear_halt(){
	/*Очистим HALT*/
	TWI_start();
	ifTWI(TW_START);
	TWI_send_data_nack(M41_addr|TW_WRITE);
	ifTWI(TW_MT_SLA_ACK);
	TWI_send_data_nack(0x0c);
	ifTWI(TW_MT_DATA_ACK);
	TWI_send_data_nack(0);
	ifTWI(TW_MT_DATA_ACK);
	TWI_stop();
	return SUCCESS;
}
